﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;

namespace Gmantis.Controls
{
    public sealed class CommandManager
    {
        // Fields
        private static Dictionary<Type, CommandBindingCollection> _classCommandBindings = new Dictionary<Type, CommandBindingCollection>();
        [ThreadStatic]
        private static CommandManager _commandManager;
        private List<WeakReference> _requerySuggestedHandlers;
        private DispatcherOperation _requerySuggestedOperation;

        // Events
        public static event EventHandler RequerySuggested
        {
            add
            {
                AddWeakReferenceHandler(ref Current._requerySuggestedHandlers, value);
            }
            remove
            {
                RemoveWeakReferenceHandler(Current._requerySuggestedHandlers, value);
            }
        }

        // Methods
        static CommandManager()
        {
            if (!PlatformIndependent.IsInDesignMode(new Border()) && (Application.Current.RootVisual == null))
            {
                Extensions.OnRootVisualSetted(() => InvalidateRequerySuggested());
            }
        }

        private CommandManager()
        {
        }

        internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
        {
            if (handlers == null)
            {
                handlers = new List<WeakReference>();
            }
            handlers.Add(new WeakReference(handler));
        }

        internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                EventHandler[] handlerArray = new EventHandler[handlers.Count];
                int index = 0;
                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler target = reference.Target as EventHandler;
                    if (target == null)
                    {
                        handlers.RemoveAt(i);
                    }
                    else
                    {
                        handlerArray[index] = target;
                        index++;
                    }
                }
                for (int j = 0; j < index; j++)
                {
                    EventHandler handler2 = handlerArray[j];
                    handler2(null, EventArgs.Empty);
                }
            }
            CommandExtensions.UpdateCanExecute();
        }

        private static bool CanExecuteCommandBinding(object sender, CanExecuteRoutedEventArgs e, CommandBinding commandBinding)
        {
            commandBinding.OnCanExecute(sender, e);
            if (!e.CanExecute)
            {
                return e.Handled;
            }
            return true;
        }

        private static bool ExecuteCommand(Command routedCommand, object parameter, UIElement target)
        {
            return routedCommand.ExecuteCore(parameter, target);
        }

        private static bool ExecuteCommandBinding(object sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
        {
            commandBinding.OnExecuted(sender, e);
            return e.Handled;
        }

        private static void FindCommandBinding(object sender, RoutedEventArgs e, ICommand command, bool execute)
        {
            lock (((ICollection)_classCommandBindings).SyncRoot)
            {
                Command command2 = command as Command;
                if ((command2 != null) && (command2.OwnerType != null))
                {
                    Type ownerType = command2.OwnerType;
                    if (_classCommandBindings.ContainsKey(ownerType))
                    {
                        CommandBindingCollection commandBindings = _classCommandBindings[ownerType];
                        if (commandBindings != null)
                        {
                            FindCommandBinding(commandBindings, sender, e, command, execute);
                            if ((!execute && ((CanExecuteRoutedEventArgs)e).CanExecute) || (!execute || !((ExecutedRoutedEventArgs)e).Handled))
                            {
                            }
                        }
                    }
                }
                else
                {
                    for (Type type2 = sender.GetType(); type2 != null; type2 = type2.BaseType)
                    {
                        if (_classCommandBindings.ContainsKey(type2))
                        {
                            CommandBindingCollection bindings2 = _classCommandBindings[type2];
                            if (bindings2 != null)
                            {
                                FindCommandBinding(bindings2, sender, e, command, execute);
                                if ((!execute && ((CanExecuteRoutedEventArgs)e).CanExecute) || (execute && ((ExecutedRoutedEventArgs)e).Handled))
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        private static void FindCommandBinding(CommandBindingCollection commandBindings, object sender, RoutedEventArgs e, ICommand command, bool execute)
        {
            CommandBinding binding;
            int index = 0;
            do
            {
                binding = commandBindings.FindMatch(command, ref index);
            }
            while (((binding != null) && (!execute || !ExecuteCommandBinding(sender, (ExecutedRoutedEventArgs)e, binding))) && (execute || !CanExecuteCommandBinding(sender, (CanExecuteRoutedEventArgs)e, binding)));
        }

        internal static Type GetRegisteredType(string typeName)
        {
            if (!string.IsNullOrEmpty(typeName))
            {
                typeName = typeName.Trim();
                int index = typeName.IndexOf(":", StringComparison.Ordinal);
                if (index >= 0)
                {
                    typeName = typeName.Substring(index + 1);
                }
                foreach (Type type in _classCommandBindings.Keys)
                {
                    if (type.FullName.EndsWith(typeName))
                    {
                        return type;
                    }
                }
            }
            return null;
        }

        public static void InvalidateRequerySuggested()
        {
            Current.RaiseRequerySuggested();
        }

        internal static void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (((sender != null) && (e != null)) && (e.Command != null))
            {
                FindCommandBinding(sender, e, e.Command, false);
                if (!e.Handled && (e.EventName == "CanExecuteEvent"))
                {
                    UIElement reference = sender as UIElement;
                    while (((reference = (UIElement)VTreeHelper.GetParentOfType(reference, typeof(UIElement), true)) != null) && !e.Handled)
                    {
                        FindCommandBinding(reference, e, e.Command, false);
                    }
                }
            }
        }

        internal static void OnExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (((sender != null) && (e != null)) && (e.Command != null))
            {
                FindCommandBinding(sender, e, e.Command, true);
                if (!e.Handled && (e.EventName == "ExecutedEvent"))
                {
                    UIElement reference = sender as UIElement;
                    while (((reference = (UIElement)VTreeHelper.GetParentOfType(reference, typeof(UIElement), true)) != null) && !e.Handled)
                    {
                        FindCommandBinding(reference, e, e.Command, true);
                    }
                }
            }
        }

        private void RaiseRequerySuggested()
        {
            Action a = null;
            if (this._requerySuggestedOperation == null)
            {
                Dispatcher dispatcher = null;
                if (Deployment.Current != null)
                {
                    dispatcher = Deployment.Current.Dispatcher;
                }
                if (dispatcher != null)
                {
                    if (a == null)
                    {
                        a = delegate
                        {
                            this._requerySuggestedOperation = null;
                            CallWeakReferenceHandlers(this._requerySuggestedHandlers);
                        };
                    }
                    this._requerySuggestedOperation = dispatcher.BeginInvoke(a);
                }
            }
        }

        public static void RegisterClassCommandBinding(Type type, CommandBinding commandBinding)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            if (commandBinding == null)
            {
                throw new ArgumentNullException("commandBinding");
            }
            lock (((ICollection)_classCommandBindings).SyncRoot)
            {
                CommandBindingCollection bindings = null;
                if (_classCommandBindings.ContainsKey(type))
                {
                    bindings = _classCommandBindings[type];
                }
                else
                {
                    bindings = new CommandBindingCollection();
                    _classCommandBindings[type] = bindings;
                }
                bindings.Add(commandBinding);
            }
            CommandExtensions.ResolveDeferredCommands();
        }

        internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
        {
            if (handlers != null)
            {
                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler target = reference.Target as EventHandler;
                    if ((target == null) || (target == handler))
                    {
                        handlers.RemoveAt(i);
                    }
                }
            }
        }

        // Properties
        private static CommandManager Current
        {
            get
            {
                if (_commandManager == null)
                {
                    _commandManager = new CommandManager();
                }
                return _commandManager;
            }
        }
    }
}
